package com.huiquan.slang.service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;

import com.huiquan.analysis.constants.AnalysisSentenceConstant;
import com.huiquan.analysis.domain.User;
import com.huiquan.foundation.util.BusinessUtil;
import com.huiquan.framework.base.BaseService;
import com.huiquan.framework.base.ReturnCode;
import com.huiquan.framework.base.ReturnData;
import com.huiquan.framework.utils.GetListUtil;
import com.huiquan.framework.utils.ReturnUtil;
import com.huiquan.slang.dao.SlangPackageDao;
import com.huiquan.slang.dao.SlangSentenceDao;
import com.huiquan.slang.dao.SlangVocabularyDao;
import com.huiquan.slang.domain.SlangPackage;
import com.huiquan.slang.domain.SlangSentence;
import com.huiquan.slang.domain.SlangVocabulary;

@Service
public class SlangSentenceService extends BaseService {

	@Autowired
	private SlangSentenceDao slangSentenceDao;
	@Autowired
	private SlangPackageDao slangPackageDao;
	@Autowired
	private SlangVocabularyDao slangVocabularyDao;

	public static final String TYPE_SPLIT = "1";
	public static final String TYPE_MERGE = "2";

	@Value("${dict.update.qa}")
	private String dictUpdateQa;

	@Value("${zuci.tmpfile.path}")
	private String zuciTmpFilePath;

	@Value("${translate.script}")
	private String translateScript;

	@Value("${zuci.script}")
	private String zuciScript;

	public ModelAndView getList(Integer start, String keyword, String keyUname, User user, String property,
			String oppositePropertyFlag, String tabid) {
		Map<String, Object> param = new HashMap<>();

		if (keyword != null && !keyword.isEmpty()) {
			if (property != null && !property.isEmpty()) {
				param.put("keyword", keyword);
			} else {
				String[] keywords = keyword.trim().split("");
				StringBuffer sb = new StringBuffer();
				sb.append("%");
				for (String temp : keywords) {
					sb.append(temp);
					sb.append("%");
				}
				param.put("keywordAll", sb.toString());
			}
		}

		if (keyUname != null && !keyUname.isEmpty()) {
			param.put("keyUname", keyUname);
		}
		if (property != null && !property.isEmpty()) {
			param.put("property", property);
		}
		param.put("oppositePropertyFlag", oppositePropertyFlag);
		if (user.getRole() != 1) {
			param.put("labelUid", user.getUserId());
		}

		int totalSize = 0;
		if (StringUtils.equalsIgnoreCase(tabid, "28_2")) {
			param.put("orderStr", "gmt_modified desc");
			param.put("labelStatus", SlangSentence.STATUS_LABELED);
			totalSize = slangSentenceDao.retrieveSize(param);
			LOGGER.info("已标注大白话句子一共有{}条", totalSize);
		} else if (StringUtils.equalsIgnoreCase(tabid, "28_3")) {
			// 获取已删除的数据
			param.put("delFlag", SlangSentence.DELFLAG_Y);
			param.put("orderStr", "gmt_modified desc");
			totalSize = slangSentenceDao.retrieveSize(param);
		} else if (StringUtils.equalsIgnoreCase(tabid, "28_6")) {
			// 获取已标注，分词都是AD的句子
			param.put("labelStatus", SlangSentence.STATUS_LABELED);
			param.put("vPro", "AD");
			param.put("orderStr", "gmt_modified desc");

			totalSize = slangSentenceDao.retrieveSize(param);
			LOGGER.info("已标注，分词都是AD的大白话句子一共有{}条", totalSize);
		} else {
			param.put("orderStr", "id");
			param.put("labelStatus", SlangSentence.STATUS_NOT_LABELED);
			totalSize = slangSentenceDao.retrieveSize(param);
		}

		Map<String, Integer> pageNo = GetListUtil.getPageNoMap(totalSize, start, -1);

		int endPage = pageNo.get("endPage");
		start = pageNo.get("start");
		int startIndex = pageNo.get("startIndex");

		List<SlangSentence> list = new ArrayList<>();

		if (startIndex < totalSize) {
			param.put("startIndex", startIndex);
			list = slangSentenceDao.retrieveList(param);
		}

		Map<String, String> searchMap = new HashMap<>();
		searchMap.put("keyword", keyword);
		searchMap.put("keyUname", keyUname);
		searchMap.put("propertyKey", property);
		searchMap.put("oppositePropertyFlag", oppositePropertyFlag);
		Map<String, Object> map = GetListUtil.getReturnMap2(totalSize, start, startIndex, endPage, list, searchMap);
		return new ModelAndView("slang/index", map);
	}

	public ModelAndView preEdit(Integer start, String keywordStr, String keyUname, Long id, User user,
			String propertyKey, String oppositePropertyFlag) {
		// 获取句子和分词的列表
		SlangSentence sentence = slangSentenceDao.retrieveObjectById(id);
		List<SlangVocabulary> vocabularys = slangVocabularyDao.retrieveListBySid(id);

		Map<String, String> characterMap = new LinkedHashMap<>();
		characterMap.putAll(AnalysisSentenceConstant.getPropertyMapByType(AnalysisSentenceConstant.CHARACTER_LABEL));
		Map<String, String> propertyMap = new LinkedHashMap<>();
		propertyMap.putAll(AnalysisSentenceConstant.getPropertyMapByType(AnalysisSentenceConstant.PHYSICAL_PROPERTY_LABEL));

		for (SlangVocabulary term : vocabularys) {
			if (!characterMap.containsKey(term.getCharacteristic())) {
				characterMap.put(term.getCharacteristic(), term.getCharacteristic());
			}
			if (!propertyMap.containsKey(term.getProperty())) {
				propertyMap.put(term.getProperty(), term.getProperty());
			}
		}

		Map<String, Object> map = new HashMap<>();
		map.put("id", id);
		map.put("sentence", sentence.getSentence());
		map.put("vocabularys", vocabularys);
		map.put("characterMap", characterMap);
		map.put("propertyMap", propertyMap);
		map.put("start", start);
		if (org.apache.commons.lang.StringUtils.isNotEmpty(keywordStr)) {
			map.put("keyword", keywordStr);
		} else {
			map.put("keyword", "");
		}
		if (org.apache.commons.lang.StringUtils.isNotEmpty(keyUname)) {
			map.put("keyUname", keyUname);
		} else {
			map.put("keyUname", "");
		}
		if (org.apache.commons.lang.StringUtils.isNotEmpty(propertyKey)) {
			map.put("propertyKey", propertyKey);
		} else {
			map.put("propertyKey", "");
		}
		map.put("oppositePropertyFlag", oppositePropertyFlag);

		return new ModelAndView("slang/sentence_edit", map);
	}

	public ModelAndView currentPackage(User user) {
		List<SlangSentence> list = new ArrayList<>();
		Long uid = user.getUserId();

		// 获取当前用户的包
		SlangPackage slangPackage = getCurrentPackage(uid);
		if (slangPackage != null) {
			// 获取该包下的所有句子
			list = getCurrentSentences(slangPackage.getSids(), uid);
		}

		// 返回页面
		Map<String, Object> data = new HashMap<>();
		data.put("list", list);
		return new ModelAndView("slang/index", data);
	}

	private List<SlangSentence> getCurrentSentences(String sids, Long uid) {
		return slangSentenceDao.retrieveListBySids(sids, uid);
	}

	private SlangPackage getCurrentPackage(Long userId) {
		// 查询该用户当前是否有未完成的包
		SlangPackage unfinished = getUnfinishedPackage(userId);
		if (unfinished != null) {
			return unfinished;
		}

		// 没有未完成的包则分配新的包
		int updSize = slangPackageDao.updateOnePackageToUser(userId);
		if (updSize > 0) {
			return getUnfinishedPackage(userId);
		}

		// 如果修改数据条数为0表示已无包分配
		return null;
	}

	private SlangPackage getUnfinishedPackage(Long userId) {
		Map<String, Object> param = new HashMap<>();
		param.put("uid", userId);
		param.put("status", SlangPackage.STATUS_UNFINISHED);
		List<SlangPackage> list = slangPackageDao.retrieveList(param);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	public ReturnData edit(Long id, String[] vocabularys, String[] characterStrs, String[] propertyStrs, User user) {
		// 获取句子
		SlangSentence sentence = slangSentenceDao.retrieveObjectById(id);

		// 获取标注前的分词
		List<SlangVocabulary> rawVocabularys = slangVocabularyDao.retrieveListBySid(id);

		// 拼凑标注后的分词
		List<SlangVocabulary> newVocabularys = new ArrayList<>();
		for (int i = 0; i < vocabularys.length; i++) {
			SlangVocabulary sv = new SlangVocabulary();
			sv.setSid(id);
			sv.setVocabulary(vocabularys[i]);
			sv.setCharacteristic(characterStrs[i]);
			sv.setProperty(propertyStrs[i]);
			sv.setInitial(vocabularys[i].substring(0, 1));
			newVocabularys.add(sv);
		}

		// 修改句子，重新填入分词
		try {
			slangSentenceDao.labelSentence(sentence, newVocabularys, user);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 调用多线程执行词典中的脚本
		Thread t = new Thread(new UpdateScript(rawVocabularys, newVocabularys, sentence, user));
		t.start();

		return ReturnUtil.success();
	}

	class UpdateScript implements Runnable {

		List<SlangVocabulary> newVocabularys;
		List<SlangVocabulary> rawVocabularys;
		SlangSentence sentence;
		User user;

		public UpdateScript(List<SlangVocabulary> rawVocabularys, List<SlangVocabulary> newVocabularys,
				SlangSentence sentence, User user) {
			super();
			this.rawVocabularys = rawVocabularys;
			this.newVocabularys = newVocabularys;
			this.sentence = sentence;
			this.user = user;
		}

		@Override
		public void run() {
			Long sid = sentence.getId();

			// 存储标注前后差异的列表
			Map<String, String> vocabularyModifys = new HashMap<>();
			vocabularyModifys.put(TYPE_MERGE, ""); // 合成差异
			vocabularyModifys.put(TYPE_SPLIT, ""); // 拆分差异

			// 获取前后分词的差异，调用脚本修改词库
			int newIndex = 0, rawIndex = 0, index = 0;
			String newStr = "", rawStr = "";
			boolean mergeFlag = true;
			// 设定前后分词各自的游标，从头开始一点点对比
			while (newIndex < newVocabularys.size() && rawIndex < rawVocabularys.size()) {
				SlangVocabulary rawVocabulary = rawVocabularys.get(rawIndex);
				SlangVocabulary newVocabulary = newVocabularys.get(newIndex);

				if (rawVocabulary.getVocabulary().equals(newVocabulary.getVocabulary())) {
					// 如果两个分词相同
					if (!rawVocabulary.getCharacteristic().equals(newVocabulary.getCharacteristic())) {
						// 属性不同，当做合成词来做
						if (vocabularyModifys.get(TYPE_MERGE).isEmpty()) {
							vocabularyModifys.put(TYPE_MERGE,
									rawVocabulary.getVocabulary() + "," + newVocabulary.getCharacteristic());
						} else {
							vocabularyModifys.put(TYPE_MERGE, vocabularyModifys.get(TYPE_MERGE) + "."
									+ rawVocabulary.getVocabulary() + "," + newVocabulary.getCharacteristic());
						}
					}
				} else {
					// 多对多的拆分属于拆词
					newStr = newVocabulary.getVocabulary();
					rawStr = rawVocabulary.getVocabulary();
					index = newIndex;
					while (!newStr.equals(rawStr)) {
						if (rawStr.contains(newStr)) {
							// 如果老短语包含新短语，则新短语继续加长，此时则不是组合类型
							newStr += newVocabularys.get(++newIndex).getVocabulary();
							mergeFlag = false;
						} else if (newStr.contains(rawStr)) {
							// 如果新短语包含老短语，则老短语继续加长
							rawStr += rawVocabularys.get(++rawIndex).getVocabulary();
						} else {
							// 如果新老短语互不包含，则异常
							LOGGER.error("SlangSentence update error 1, sid=[{}], rawStr=[{}], newStr=[{}]",
									new Object[] { sid, rawStr, newStr });
							break;
						}
					}

					// 循环结束后newStr与rawStr必定相同，如果不同则有异常，打出日志
					if (!newStr.equals(rawStr)) {
						LOGGER.error("Sentence update error 2, sid=[{}], rawStr=[{}], newStr=[{}]",
								new Object[] { sid, rawStr, newStr });
					} else {
						if (mergeFlag) {
							// 组合情况
							if (vocabularyModifys.get(TYPE_MERGE).isEmpty()) {
								vocabularyModifys.put(TYPE_MERGE, newStr + "," + newVocabulary.getCharacteristic());
							} else {
								vocabularyModifys.put(TYPE_MERGE, vocabularyModifys.get(TYPE_MERGE) + "." + newStr + ","
										+ newVocabulary.getCharacteristic());
							}
						} else {
							// 拆词情况
							StringBuilder builder = new StringBuilder(rawStr);
							for (; index <= newIndex; index++) {
								builder.append(",").append(newVocabularys.get(index).getVocabulary()).append(",")
										.append(newVocabularys.get(index).getCharacteristic());
							}
							if (vocabularyModifys.get(TYPE_SPLIT).isEmpty()) {
								vocabularyModifys.put(TYPE_SPLIT, builder.toString());
							} else {
								vocabularyModifys.put(TYPE_SPLIT,
										vocabularyModifys.get(TYPE_SPLIT) + "." + builder.toString());
							}
						}
					}

					// 临时变量还原
					newStr = "";
					rawStr = "";
					mergeFlag = true;
				}
				newIndex++;
				rawIndex++;
			}

			// 调用脚本
			for (String type : vocabularyModifys.keySet()) {
				if (StringUtils.isNotBlank(vocabularyModifys.get(type))) {
					String cmds[] = new String[5];
					cmds[0] = dictUpdateQa;
					cmds[1] = sentence.getSentence();
					cmds[2] = vocabularyModifys.get(type);
					cmds[3] = type;
					cmds[4] = user.getRealName();

					// 定义shell返回值
					String result = "error";
					try {
						result = BusinessUtil.excuteShell(cmds);
					} catch (IOException e) {
						e.printStackTrace();
					}
					LOGGER.info("dict_update_script result=" + result);
				}
			}
		}
	}

	public ReturnData preview(Long id, String[] vocabularyStrs, String[] characterStrs, String[] propertyStrs)
			throws Exception {
		// 组成分词列表
		List<SlangVocabulary> svs = new ArrayList<>();
		for (int i = 0; i < vocabularyStrs.length; i++) {
			SlangVocabulary sv = new SlangVocabulary();
			sv.setVocabulary(vocabularyStrs[i]);
			sv.setCharacteristic(characterStrs[i]);
			sv.setProperty(propertyStrs[i]);
			svs.add(sv);
		}

		// 获取句子
		SlangSentence sentence = slangSentenceDao.retrieveObjectById(id);

		String csStr = System.currentTimeMillis() + ".txt";

		File file = new File(zuciTmpFilePath + csStr);
		OutputStream fOS = new FileOutputStream(file);
		BufferedOutputStream bOS = new BufferedOutputStream(fOS);

		for (SlangVocabulary vocabulary : svs) {
			bOS.write((vocabulary.getVocabulary()).getBytes("utf-8"));
			bOS.write(
					("\t" + vocabulary.getCharacteristic() + "\t" + vocabulary.getProperty() + "\n").getBytes("utf-8"));
		}

		bOS.flush();
		bOS.close();

		String cmds[] = new String[3];
		cmds[0] = translateScript;
		cmds[1] = file.getAbsolutePath();
		cmds[2] = csStr;

		// 定义shell返回值
		String result = BusinessUtil.excuteShell(cmds);

		String splitStr = result.split("\t")[1];
		file.delete();
		fOS = new FileOutputStream(file);
		bOS = new BufferedOutputStream(fOS);

		bOS.write((sentence.getSentence() + String.valueOf((char) 1) + "1" + "\t" + splitStr + "\n").getBytes("utf-8"));
		bOS.flush();
		bOS.close();

		cmds = new String[3];
		cmds[0] = zuciScript;
		cmds[1] = file.getAbsolutePath();
		cmds[2] = csStr;
		result = BusinessUtil.excuteShell(cmds);

		Map<String, Object> data = new HashMap<>();
		data.put("preview", result);
		return ReturnUtil.success(data);
	}

	public ReturnData submitPackage(User user) {
		// 获取当前用户的包
		SlangPackage slangPackage = getCurrentPackage(user.getUserId());

		// 检查该包是否已完成
		int unlabeledCount = slangSentenceDao.retrieveUnlabeledSizeByIds(slangPackage.getSids());
		if (unlabeledCount > 0) {
			return ReturnUtil.fail(ReturnCode.PACKAGE_SENTENCE_UNFINISHED);
		}

		// 标注完成则修改包内容
		slangPackage.setStatus(SlangPackage.STATUS_FINISHED);
		slangPackageDao.update(slangPackage);

		return ReturnUtil.success();
	}

	public ReturnData delete(long id, User user) {
		SlangSentence sentence = new SlangSentence();
		sentence.setId(id);
		sentence.setModifierId(user.getUserId());
		sentence.setModifierName(user.getRealName());
		slangSentenceDao.delete(sentence);

		return ReturnUtil.success();
	}

	public ReturnData restore(long id, User user) {
		SlangSentence sentence = new SlangSentence();
		sentence.setId(id);
		sentence.setModifierId(user.getUserId());
		sentence.setModifierName(user.getRealName());
		slangSentenceDao.restore(sentence);

		return ReturnUtil.success();
	}
}
